//
//  StoryController.swift
//  App
//
//  Created by 汤泽川 on 2019/4/14.
//

import Foundation
import Vapor
import FluentMySQL

class StoryController : RouteCollection {
    func boot(router: Router) throws {
        router.post(RequestModel<FetchStoryRequest>.self, at: "query", use: fetchStoryInfo)
        router.post("category", use: getStoryCategory)
        router.post(RequestModel<SearchStoryInfo>.self, at: "search", use: searchStoryInfo)
        router.post(RequestModel<QueryCollectionStoryStateRequest>.self, at: "query_collection_state", use: queryCollectionState)
        router.post(RequestModel<SearchAuthorStoryInfo>.self, at: "author", use: searchAuthorStoryInfo)
        
        let userAuthRouter = router.grouped(UserAuthMiddleware())
        userAuthRouter.post(RequestModel<UploadStoryRequest>.self, at: "upload", use: uploadStory)
        userAuthRouter.post(RequestModel<CollectionStoryRequest>.self, at: "collection", use: changeCollectionState)
        userAuthRouter.post(RequestModel<QueryCollectionStoryRequest>.self, at: "query_collection_story", use: queryCollectionStory)
    }
}

extension StoryController {
    public func fetchStoryInfo(_ req : Request, _ content : RequestModel<FetchStoryRequest>) throws -> Future<Response> {
        var query = StoryInfo.query(on: req).join(\UserInfo.username, to: \StoryInfo.authorName)
        if content.data.channelId != 0 {
            query = query.filter(\.channelId == content.data.channelId)
        }
        return query.range(lower: content.data.page * content.data.count, upper:(content.data.page + 1 ) * content.data.count - 1).sort(\StoryInfo.updatedAt, .descending).alsoDecode(UserInfo.self).all().flatMap {
            result in
            return result.map {
                item in
                return CollectionStoryInfo.query(on: req).filter(\CollectionStoryInfo.storyId == item.0.id!).count()
                }.flatMap(to: Response.self, on: req) { collectionCounts in
                    return result.map {
                        item in
                        return CommentModel.query(on: req).filter(\CommentModel.storyId == item.0.id!).count()
                        }.flatMap(to: Response.self, on: req) { commentCounts in
                            var datas = [StoryInfoResponse]()
                            for (index, item) in result.enumerated() {
                                datas.append(StoryInfoResponse(storyInfo: item.0, author: item.1, collectionCount: collectionCounts[index], commentCount: commentCounts[index]))
                            }
                            return try ResponseModel<[StoryInfoResponse]>.successResponse(datas).encode(for: req)
                    }
            }
        }
    }
    
    public func uploadStory(_ req : Request, _ content : RequestModel<UploadStoryRequest>) throws -> Future<Response> {
        let uploadInfo = content.data
        return try UserAccessToken.queryUserInfo(accessToken: content.accessToken!, req: req).flatMap {
            userInfo in
            return StoryInfo(id: nil, createdAt: nil, updatedAt: nil, storyName: uploadInfo.storyName, introduce: uploadInfo.introduce, avatarUrl: uploadInfo.avatarUrl, channelId: uploadInfo.channelId, storyUrl: uploadInfo.storyUrl, authorName: userInfo.username).save(on: req).flatMap {
                result in
                return try ResponseModel<String>.successResponse().encode(for: req)
            }
        }
    }
    
    public func getStoryCategory(_ req : Request) throws -> Future<Response> {
        return StoryCategory.query(on: req).all().flatMap {
            categorys in
            return try ResponseModel<[StoryCategory]>.successResponse(categorys).encode(for: req)
        }
    }
    
    public func searchStoryInfo(_ req : Request, _ content : RequestModel<SearchStoryInfo>) throws -> Future<Response> {
        return StoryInfo.query(on: req).join(\UserInfo.username, to: \StoryInfo.authorName).group(.or, closure: { (builder) in
            builder.filter(\StoryInfo.storyName, .like, "%\(content.data.keyword)%")
            builder.filter(\StoryInfo.introduce, .like, "%\(content.data.keyword)%")
            builder.filter(\StoryInfo.authorName, .like, "%\(content.data.keyword)%")
        }).range(lower: content.data.page * content.data.count, upper:(content.data.page + 1 ) * content.data.count - 1).sort(\StoryInfo.updatedAt, .descending).alsoDecode(UserInfo.self).all().flatMap {
            result in
            return result.map {
                item in
                return CollectionStoryInfo.query(on: req).filter(\CollectionStoryInfo.storyId == item.0.id!).count()
                }.flatMap(to: Response.self, on: req) { collectionCounts in
                    return result.map {
                        item in
                        return CommentModel.query(on: req).filter(\CommentModel.storyId == item.0.id!).count()
                        }.flatMap(to: Response.self, on: req) { commentCounts in
                            var datas = [StoryInfoResponse]()
                            for (index, item) in result.enumerated() {
                                datas.append(StoryInfoResponse(storyInfo: item.0, author: item.1, collectionCount: collectionCounts[index], commentCount: commentCounts[index]))
                            }
                            return try ResponseModel<[StoryInfoResponse]>.successResponse(datas).encode(for: req)
                    }
            }
        }
    }
    
    public func searchAuthorStoryInfo(_ req : Request, _ content : RequestModel<SearchAuthorStoryInfo>) throws -> Future<Response> {
        return StoryInfo.query(on: req).join(\UserInfo.username, to: \StoryInfo.authorName).filter(\StoryInfo.authorName == content.data.authorName).range(lower: content.data.page * content.data.count, upper:(content.data.page + 1 ) * content.data.count - 1).sort(\StoryInfo.updatedAt, .descending).alsoDecode(UserInfo.self).all().flatMap {
            result in
            return result.map {
                item in
                return CollectionStoryInfo.query(on: req).filter(\CollectionStoryInfo.storyId == item.0.id!).count()
                }.flatMap(to: Response.self, on: req) { collectionCounts in
                    return result.map {
                        item in
                        return CommentModel.query(on: req).filter(\CommentModel.storyId == item.0.id!).count()
                        }.flatMap(to: Response.self, on: req) { commentCounts in
                            var datas = [StoryInfoResponse]()
                            for (index, item) in result.enumerated() {
                                datas.append(StoryInfoResponse(storyInfo: item.0, author: item.1, collectionCount: collectionCounts[index], commentCount: commentCounts[index]))
                            }
                            return try ResponseModel<[StoryInfoResponse]>.successResponse(datas).encode(for: req)
                    }
            }
        }
    }
    
    public func changeCollectionState(_ req : Request, _ content : RequestModel<CollectionStoryRequest>) throws -> Future<Response> {
        return try UserAccessToken.queryUserInfo(accessToken: content.accessToken!, req: req).flatMap{ userInfo in
            return CollectionStoryInfo.query(on: req).filter(\.userId == userInfo.id!).filter(\.storyId == content.data.storyId).first().flatMap {
                info in
                if info == nil && content.data.state == 1 {
                    _ = CollectionStoryInfo(id: nil, createdAt: nil, updatedAt: nil, userId: userInfo.id!, storyId: content.data.storyId).save(on: req)
                }
                if info != nil && content.data.state == 0 {
                    _ = info!.delete(on: req)
                }
                return try ResponseModel<String>.successResponse().encode(for: req)
            }
        }
    }
    
    public func queryCollectionState(_ req : Request, _ content : RequestModel<QueryCollectionStoryStateRequest>) throws -> Future<Response> {
        guard let accessToken = content.accessToken else {
            return try ResponseModel<QueryCollectionStoryStateResponse>.successResponse(QueryCollectionStoryStateResponse(state: 0)).encode(for: req)
        }
        return try UserAccessToken.queryUserInfo(accessToken: accessToken, req: req).flatMap {
            userInfo in
            return CollectionStoryInfo.query(on: req).filter(\.storyId == content.data.storyId).filter(\.userId == userInfo.id!).first().flatMap {
                collectionInfo in
                if collectionInfo == nil {
                    return try ResponseModel<QueryCollectionStoryStateResponse>.successResponse(QueryCollectionStoryStateResponse(state: 0)).encode(for: req)
                } else {
                    return try ResponseModel<QueryCollectionStoryStateResponse>.successResponse(QueryCollectionStoryStateResponse(state: 1)).encode(for: req)
                }
            }
        }
    }
    
    public func queryCollectionStory(_ req : Request, _ content : RequestModel<QueryCollectionStoryRequest>) throws -> Future<Response> {
        return try UserAccessToken.queryUserInfo(accessToken: content.accessToken!, req: req).flatMap {
            userInfo in
            return CollectionStoryInfo.query(on: req).join(\StoryInfo.id, to: \CollectionStoryInfo.storyId).join(\UserInfo.username, to: \StoryInfo.authorName).filter(\CollectionStoryInfo.userId == userInfo.id!).alsoDecode(StoryInfo.self).alsoDecode(UserInfo.self).range(lower: content.data.page * content.data.count, upper:(content.data.page + 1 ) * content.data.count - 1).sort(\StoryInfo.updatedAt, .descending).all().flatMap {
                result in
                return result.map {
                    item in
                    return CollectionStoryInfo.query(on: req).filter(\.storyId == item.0.1.id!).count()
                    }.flatMap(to: Response.self, on: req) { collectionCounts in
                        return result.map {
                            item in
                            return CommentModel.query(on: req).filter(\.storyId == item.0.1.id!).count()
                            }.flatMap(to: Response.self, on: req) { commentCounts in
                                var datas = [StoryInfoResponse]()
                                for (index, item) in result.enumerated() {
                                    datas.append(StoryInfoResponse(storyInfo: item.0.1, author: item.1, collectionCount: collectionCounts[index], commentCount: commentCounts[index]))
                                }
                                return try ResponseModel<[StoryInfoResponse]>.successResponse(datas).encode(for: req)
                            }
                    }
            }
        }
    }
    
}
